#include "../include/c0.h"
#include <iostream>
#include <string>
#include <sstream>

using namespace std;

const int stackSize = 500;

int block() {
    gen(fct::JMP, 0, 18);
    gen(fct::JMP, 0, 3);
    gen(fct::JMP, 0, 10);
    gen(fct::INT, 0, 3);
    gen(fct::LOD, 2, 2);
    gen(fct::LOD, 2, 3);
    gen(fct::ADD, 0, 0);
    gen(fct::STO, 0, 2);
    gen(fct::LOD, 0, 2);
    gen(fct::RET, 0, 0);
    gen(fct::INT, 0, 2);
    gen(fct::RED, 0, 0);
    gen(fct::STO, 1, 2);
    gen(fct::RED, 0, 0);
    gen(fct::STO, 1, 3);
    gen(fct::CAL, 0, 1);
    gen(fct::WRT, 0, 0);
    gen(fct::RET, 0, 0);
    gen(fct::INT, 0, 4);
    gen(fct::CAL, 0, 2);
    gen(fct::RET, 0, 0);
    return 0;
}

int main() {
    FILE *f;
    f= fopen("../test/lessthan.c0","r");
    yyrestart(f);
    yyparse();
    fa.open("fa.txt", ios::out);
    block();
    fa.close();
    fa2.open("fa2.txt");
    Interpreter::interpret();
    fa2.close();
    return 0;
}

fct strToFct(const string &str) {
    for (pair<fct, string> item: fctMap) {
        if (item.second == str)
            return item.first;
    }
    return fct::JPC;
}

string fctToStr(fct f) {
    return fctMap.at(f);
}

void Interpreter::read() {
    fa.open("fa.txt");
    string line;
    string str1, str2, str3;
    if (fa) {
        while (getline(fa, line)) {
            istringstream is(line);
            is >> str1 >> str2 >> str3;
            code.emplace_back(strToFct(str1), stoi(str2), stoi(str3));
        }
    } else {
        cout << "Without this file!" << endl;
    }
    fa.close();
}

void Interpreter::interpret() {
    int ip;//instruction pointer指令指针
    int bp;//base pointer指令基址
    int sp;//stack pointer栈顶指针
    Interpreter i{};//存放当前指令
    int stack[stackSize];//栈
    read();
    cout << "start c0" << endl;
    ip = bp = sp = 0;
    do {
        i = code[ip];//读取当前指令
        ip++;
        switch (i.f) {
            case fct::LIT://将a的值取到栈顶
                stack[sp] = i.a;
                sp++;
                break;
            case fct::LOD://将变量值取到栈顶，a为相对地址，l为层数
                stack[sp] = stack[base(i.l, stack, bp) + i.a];
                sp++;
                break;
            case fct::STO://将栈顶内容送入某变量单元中，a为相对地址，l为层数
                sp--;
                stack[base(i.l, stack, bp) + i.a] = stack[sp];
                break;
            case fct::CAL://调动地址为a的过程，不可嵌套
                stack[sp] = bp;
                stack[sp + 1] = ip;
                bp = sp;
                ip = i.a;
                break;
            case fct::INT://在运行栈中为被调用的过程开辟a个单元的数据区
                sp += i.a;
                break;
            case fct::JMP://无条件转移至地址a
                ip = i.a;
                break;
            case fct::JPC://若栈顶为0，则转移至地址a
                sp--;
                if (stack[sp] == 0) {
                    ip = i.a;
                }
                break;
            case fct::ADD://此栈顶与栈顶相加，结果存入次栈顶
                sp--;
                stack[sp - 1] = stack[sp - 1] + stack[sp];
                break;
            case fct::SUB://此栈顶与栈顶相减，结果存入次栈顶
                sp--;
                stack[sp - 1] = stack[sp - 1] - stack[sp];
                break;
            case fct::MUL://此栈顶与栈顶相乘，结果存入次栈顶
                sp--;
                stack[sp - 1] = stack[sp - 1] * stack[sp];
                break;
            case fct::DIV://此栈顶与栈顶相除，结果存入次栈顶
                sp--;
                stack[sp - 1] = stack[sp - 1] / stack[sp];
                break;
            case fct::RED://从命令行读入一个输入置于栈顶
                cout << "?";
                fa2 << "?";
                cin >> stack[sp];
                fa2 << stack[sp] << endl;
                sp++;
                break;
            case fct::WRT://栈顶的值输出至控制台并换行
                cout << stack[sp - 1] << endl;
                fa2 << stack[sp - 1] << endl;
                sp--;
                break;
            case fct::RET://结束被调用过程，返回调用点，退栈并将返回值存入退栈后的栈顶
                int rv;//Return value记录返回值
                rv = stack[sp - 1];
                sp = bp;
                bp = stack[sp];
                ip = stack[sp + 1];
                stack[sp] = rv;
                sp++;
                break;
        }
    } while (ip != 0);
}

int gen(fct x, int y, int z) {
    if (cx >= cxMax) {
        cout << "Program too long";
        return -1;
    }
    fa << fctToStr(x) << " " << y << " " << z << endl;
    cx++;
    return 0;
}

int base(int l, const int *s, int bp) {
    int b;
    b = bp;
    while (l > 0) {
        b = s[b];
        l--;
    }
    return b;
}
